using System.Globalization;

namespace System.Windows.Data
{
    /// <summary>
    /// Revertable <see cref="Boolean">boolean</see> to anything <see cref="IValueConverter">value converter</see>.
    /// </summary>
    public abstract class RevertibleBooleanToAnythingConverter : DependencyObject, IValueConverter
    {
        /// <summary>
        /// Dependency Property.
        /// </summary>
        public static DependencyProperty IsReversedProperty =
            DependencyProperty.Register("IsReversed",
                                        typeof (bool),
                                        typeof(RevertibleBooleanToAnythingConverter),
                                        new PropertyMetadata(false));

        /// <summary>
        /// Gets or sets optional flag that indicates whether the conversion rule should be reversed or not.
        /// </summary>
        public bool IsReversed
        {
            get { return (bool) GetValue(IsReversedProperty); }
            set { SetValue(IsReversedProperty, value); }
        }

        /// <summary>
        /// Direct value for conversion.
        /// </summary>
        protected abstract object DirectValue { get; }

        /// <summary>
        /// Opposite-to-Direct value for conversion.
        /// </summary>
        protected abstract object OppositeValue { get; }

        /// <summary>
        /// Determines whether value is DirectValue.
        /// </summary>
        /// <param name="value">Value.</param>
        /// <returns>True, if DirectValue, otherwise - false.</returns>
        protected abstract bool IsDirectValue(object value);

        #region Implementation of IValueConverter

        /// <summary>
        /// Modifies the source data before passing it to the target for display in the UI.
        /// </summary>
        /// <returns>
        /// The value to be passed to the target dependency property.
        /// </returns>
        /// <param name="value">The source data being passed to the target.</param>
        /// <param name="targetType">The <see cref="T:System.Type"/> of data expected by the target dependency property.</param>
        /// <param name="parameter">An optional parameter to be used in the converter logic.</param>
        /// <param name="culture">The culture of the conversion.</param>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var val = (bool)value;

            return IsReversed
                       ? (val ? OppositeValue : DirectValue)
                       : (val ? DirectValue : OppositeValue);
        }

        /// <summary>
        /// Modifies the target data before passing it to the source object.  This method is called only in <see cref="F:System.Windows.Data.BindingMode.TwoWay"/> bindings.
        /// </summary>
        /// <returns>
        /// The value to be passed to the source object.
        /// </returns>
        /// <param name="value">The target data being passed to the source.</param><param name="targetType">The <see cref="T:System.Type"/> of data expected by the source object.</param>
        /// <param name="parameter">An optional parameter to be used in the converter logic.</param>
        /// <param name="culture">The culture of the conversion.</param>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var result = IsDirectValue(value);

            return result ? !IsReversed : IsReversed;
        }

        #endregion
    }
}